package yu.ac.bg.etf.kdp.server;

import java.io.*;
import java.util.*;
import java.net.*;
import yu.ac.bg.etf.kdp.klase.*;

public class Server extends Thread {

	private ServerSocket serverSocket;
	protected boolean working;
	private int port;
	
	protected NodeList workstations = null;
	protected JobNodeList jobList = null;
	
	protected ServerGUI serverGUI;
	
	protected LinkedList checkersList;
	private static int checkTime;
	
	public Server () {
		this(4002, new ServerGUI());
	}
	
	public Server (int port, ServerGUI serverGUI) {
		working = false;
		checkTime = 5;
		this.serverGUI = serverGUI;
		try {
			serverSocket = new ServerSocket(port);
			System.out.println("Server started.. \nListening on port "+port);
			try {
				FileOutputStream file = new FileOutputStream("server_log.txt",true);
				PrintWriter log = new PrintWriter(file);
				log.println("Log file for server.");
				log.close();
			} catch (FileNotFoundException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			workstations = new NodeList();
			jobList = new JobNodeList();
			checkersList = new LinkedList();
			working = true;
			this.port = port;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.out.println("Server could not listen on port "+port+"..");
		}
	}
	
	public void run () {
		while (working) {
			try {
				Socket socket = serverSocket.accept();
				new ServerThread(this, socket).start();
			}  catch (SocketException e1) {
				System.out.println("Server socket closed..");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				System.out.println("Connection broken..");
				System.exit(1);
			}
		}
		System.out.println("Server finished running..");
	}
	
	public Node getFreeWorkstation () {
		Node node;
		synchronized (this) {
			boolean found = false;
			node = null;
			while (!found) {System.out.println("LOOKING FOR WORKSTATION..");
				while (workstations.size() == 0)
					try {System.out.println("WAITING FOR WORKSTATION..");
						wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				int z = 0;
				node = (Node) workstations.get(z);
				while (node.getJobCapacity() <= 0 && z < workstations.size()) {
					node = (Node) workstations.get(z++);
				}
				if (node.getJobCapacity() > 0) {
					found = true;
					node.setJobCapacity(node.getJobCapacity() - 1);
				} else {System.out.println("WAITING FOR FREE WORKSTATION..");
					try {
						wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
		return node;
	}
	
	public void insertNewJob (Job job) {
		synchronized (this) {
			jobList.add(new JobNode(job));
			serverGUI.getJobList().add("Job " + job.getId());
		}
		System.out.println("Job is being inserted in server job list..");
		System.out.println("Command is:						"+job.getCommand());
		System.out.println("Job Id is:						"+job.getId());
		System.out.println("Job status is:					"+job.getStatus());
	}	
	
	public synchronized void stopServer () {
		for (int i = 0;i < checkersList.size();i++) {
			((CheckerThreadServer)checkersList.get(i)).stopChecking();
			try {
				((CheckerThreadServer)checkersList.get(i)).join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		serverGUI.getWstaList().removeAll();
		serverGUI.getJobList().removeAll();
		working = false;
		try {
			serverSocket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.out.println("Connection broken..");
			System.exit(1);
		}
	}

	public synchronized void workstationBroken(Node workstation) {
		serverGUI.getWstaList().remove("Workstation "+workstation.getHostForServer());
		System.out.println("Workstation broken..");
		int ind = workstations.getIndexOf(workstation.getWstaServerHost(), workstation.getWstaServerPort());
		workstations.remove(ind);
		// TODO Auto-generated method stub
		// treba da pretrazimo listu poslova i da vidimo
		// da li je neki zapocela ova radna stanica ili
		// da li u nekom ucestvuje ova radna stanica
		for (int i = 0;i < jobList.size();i++) {
			Job job = ((JobNode)jobList.get(i)).getJob();
			// ako je ovaj posao zapocela pokvarena radna stanica
			// treba da ga zapocnemo opet
			if (workstation.getWstaServerHost().equals(job.getMainWorkstation().getWstaServerHost())
				&& workstation.getWstaServerPort() == job.getMainWorkstation().getWstaServerPort()) {
				job.setInvolvedWorkstations(null);
				Node node = this.getFreeWorkstation();
				Job newJob = new Job(job.getCommand(),job.getResults(),Job.JOB_READY);
				int jid = job.getId();
				newJob.setId(jid);
				newJob.setMainWorkstation(node);
				job = newJob;
				// kacimo se na njen socket koji slusa server
				Socket sock;
				try {
					sock = new Socket(node.getHostForServer(), node.getPortForServer());
					ObjectOutputStream outo1 = new ObjectOutputStream(sock.getOutputStream());
					outo1.flush();
					ObjectInputStream ino1 = new ObjectInputStream(sock.getInputStream());
					outo1.writeObject(new MsgTxt("serverRequest"));
					outo1.flush();
					outo1.writeObject(new MsgTxt("jobRequest"));
					outo1.flush();
					outo1.writeObject(job);
					outo1.flush();
					FileTransfer.sendFile(outo1, "a" + job.getId() + ".txt");
					if (job.getCommand().equals("solve") || job.getCommand().equals("calculate"))
						FileTransfer.sendFile(outo1, "b" + job.getId() + ".txt");
					job.setStatus(Job.JOB_RUNNING);
					ino1.close();
					outo1.close();
					sock.close();
				} catch (UnknownHostException e) {
					// TODO Auto-generated catch block
					System.out.println("Workstation host could not be found..");
					System.exit(1);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					System.out.println("Connection broken..");
					System.exit(1);
				}
				
			} else {
				boolean brokenJob = false;
				for (int j = 0;j < job.getInvolvedWorkstations().size();j++) {
					Node wsta = (Node)job.getInvolvedWorkstations().get(j);
					if (wsta.getWstaServerHost().equals(workstation.getWstaServerHost()) &&
						wsta.getWstaServerPort() == workstation.getWstaServerPort()) {
						brokenJob = true;
					}
				}
				if (brokenJob) {
					while (job.getStatus() != Job.JOB_FAILED)
						try {
							wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						job.setInvolvedWorkstations(null);
						Node node = this.getFreeWorkstation();
						job.setMainWorkstation(node);
						// kacimo se na njen socket koji slusa server
						Socket sock;
						try {
							sock = new Socket(node.getHostForServer(), node.getPortForServer());
							ObjectOutputStream outo1 = new ObjectOutputStream(sock.getOutputStream());
							outo1.flush();
							ObjectInputStream ino1 = new ObjectInputStream(sock.getInputStream());
							outo1.writeObject(new MsgTxt("serverRequest"));
							outo1.flush();
							outo1.writeObject(new MsgTxt("jobRequest"));
							outo1.flush();
							outo1.writeObject(job);
							outo1.flush();
							FileTransfer.sendFile(outo1, "a" + job.getId() + ".txt");
							if (job.getCommand().equals("solve") || job.getCommand().equals("calculate"))
								FileTransfer.sendFile(outo1, "b" + job.getId() + ".txt");
							job.setStatus(Job.JOB_RUNNING);
							ino1.close();
							outo1.close();
							sock.close();
						} catch (UnknownHostException e) {
							// TODO Auto-generated catch block
							System.out.println("Workstation host could not be found..");
							System.exit(1);
						} catch (IOException e) {
							// TODO Auto-generated catch block
							System.out.println("Connection broken..");
							System.exit(1);
						}
				}
			}
		}
	}
	
	public boolean isWorking () {
		return working;
	}
	
	public static void main (String[] args) {
		new Server().start();
	}

	public static void setCheckTime(int checkTime) {
		Server.checkTime = checkTime;
	}

	public static int getCheckTime() {
		return checkTime;
	}
	
}
